// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Tests for WebSocketBasicStream. Note that we do not attempt to verify that
// frame parsing itself functions correctly, as that is covered by the
// WebSocketFrameParser tests.

#include "net/websockets/websocket_basic_stream.h"

#include <stddef.h>
#include <stdint.h>
#include <string.h> // for memcpy() and memset().
#include <string>
#include <utility>
#include <vector>

#include "base/big_endian.h"
#include "base/macros.h"
#include "net/base/test_completion_callback.h"
#include "net/log/test_net_log.h"
#include "net/socket/socket_test_util.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace net {
namespace {

#define WEBSOCKET_BASIC_STREAM_TEST_DEFINE_CONSTANT(name, value) \
    const char k##name[] = value;                                \
    const size_t k##name##Size = arraysize(k##name) - 1;

    WEBSOCKET_BASIC_STREAM_TEST_DEFINE_CONSTANT(SampleFrame, "\x81\x06Sample");
    WEBSOCKET_BASIC_STREAM_TEST_DEFINE_CONSTANT(
        PartialLargeFrame,
        "\x81\x7F\x00\x00\x00\x00\x7F\xFF\xFF\xFF"
        "chromiunum ad pasco per loca insanis pullum manducat frumenti");
    const size_t kLargeFrameHeaderSize = 10;
    WEBSOCKET_BASIC_STREAM_TEST_DEFINE_CONSTANT(MultipleFrames,
        "\x81\x01X\x81\x01Y\x81\x01Z");
    WEBSOCKET_BASIC_STREAM_TEST_DEFINE_CONSTANT(EmptyFirstFrame, "\x01\x00");
    WEBSOCKET_BASIC_STREAM_TEST_DEFINE_CONSTANT(EmptyMiddleFrame, "\x00\x00");
    WEBSOCKET_BASIC_STREAM_TEST_DEFINE_CONSTANT(EmptyFinalTextFrame, "\x81\x00");
    WEBSOCKET_BASIC_STREAM_TEST_DEFINE_CONSTANT(EmptyFinalContinuationFrame,
        "\x80\x00");
    WEBSOCKET_BASIC_STREAM_TEST_DEFINE_CONSTANT(ValidPong, "\x8A\x00");
    // This frame encodes a payload length of 7 in two bytes, which is always
    // invalid.
    WEBSOCKET_BASIC_STREAM_TEST_DEFINE_CONSTANT(InvalidFrame,
        "\x81\x7E\x00\x07Invalid");
    // Control frames must have the FIN bit set. This one does not.
    WEBSOCKET_BASIC_STREAM_TEST_DEFINE_CONSTANT(PingFrameWithoutFin, "\x09\x00");
    // Control frames must have a payload of 125 bytes or less. This one has
    // a payload of 126 bytes.
    WEBSOCKET_BASIC_STREAM_TEST_DEFINE_CONSTANT(
        126BytePong,
        "\x8a\x7e\x00\x7eZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ"
        "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ");
    WEBSOCKET_BASIC_STREAM_TEST_DEFINE_CONSTANT(CloseFrame,
        "\x88\x09\x03\xe8occludo");
    WEBSOCKET_BASIC_STREAM_TEST_DEFINE_CONSTANT(WriteFrame,
        "\x81\x85\x00\x00\x00\x00Write");
    WEBSOCKET_BASIC_STREAM_TEST_DEFINE_CONSTANT(MaskedEmptyPong,
        "\x8A\x80\x00\x00\x00\x00");
    const WebSocketMaskingKey kNulMaskingKey = { { '\0', '\0', '\0', '\0' } };
    const WebSocketMaskingKey kNonNulMaskingKey = {
        { '\x0d', '\x1b', '\x06', '\x17' }
    };

    // A masking key generator function which generates the identity mask,
    // ie. "\0\0\0\0".
    WebSocketMaskingKey GenerateNulMaskingKey() { return kNulMaskingKey; }

    // A masking key generation function which generates a fixed masking key with no
    // nul characters.
    WebSocketMaskingKey GenerateNonNulMaskingKey() { return kNonNulMaskingKey; }

    // Base class for WebSocketBasicStream test fixtures.
    class WebSocketBasicStreamTest : public ::testing::Test {
    protected:
        std::unique_ptr<WebSocketBasicStream> stream_;
        TestNetLog net_log_;
    };

    // A subclass of StaticSocketDataProvider modified to require that all data
    // expected to be read or written actually is.
    class StrictStaticSocketDataProvider : public StaticSocketDataProvider {
    public:
        StrictStaticSocketDataProvider(MockRead* reads,
            size_t reads_count,
            MockWrite* writes,
            size_t writes_count,
            bool strict_mode)
            : StaticSocketDataProvider(reads, reads_count, writes, writes_count)
            , strict_mode_(strict_mode)
        {
        }

        ~StrictStaticSocketDataProvider() override
        {
            if (strict_mode_) {
                EXPECT_EQ(read_count(), read_index());
                EXPECT_EQ(write_count(), write_index());
            }
        }

    private:
        const bool strict_mode_;
    };

    // A fixture for tests which only perform normal socket operations.
    class WebSocketBasicStreamSocketTest : public WebSocketBasicStreamTest {
    protected:
        WebSocketBasicStreamSocketTest()
            : pool_(1, 1, &factory_)
            , generator_(&GenerateNulMaskingKey)
            , expect_all_io_to_complete_(true)
        {
        }

        ~WebSocketBasicStreamSocketTest() override
        {
            // stream_ has a reference to socket_data_ (via MockTCPClientSocket) and so
            // should be destroyed first.
            stream_.reset();
        }

        std::unique_ptr<ClientSocketHandle> MakeTransportSocket(MockRead reads[],
            size_t reads_count,
            MockWrite writes[],
            size_t writes_count)
        {
            socket_data_.reset(new StrictStaticSocketDataProvider(
                reads, reads_count, writes, writes_count, expect_all_io_to_complete_));
            socket_data_->set_connect_data(MockConnect(SYNCHRONOUS, OK));
            factory_.AddSocketDataProvider(socket_data_.get());

            std::unique_ptr<ClientSocketHandle> transport_socket(
                new ClientSocketHandle);
            scoped_refptr<MockTransportSocketParams> params;
            transport_socket->Init(
                "a", params, MEDIUM, ClientSocketPool::RespectLimits::ENABLED,
                CompletionCallback(), &pool_, bound_net_log_.bound());
            return transport_socket;
        }

        void SetHttpReadBuffer(const char* data, size_t size)
        {
            http_read_buffer_ = new GrowableIOBuffer;
            http_read_buffer_->SetCapacity(size);
            memcpy(http_read_buffer_->data(), data, size);
            http_read_buffer_->set_offset(size);
        }

        void CreateStream(MockRead reads[],
            size_t reads_count,
            MockWrite writes[],
            size_t writes_count)
        {
            stream_ = WebSocketBasicStream::CreateWebSocketBasicStreamForTesting(
                MakeTransportSocket(reads, reads_count, writes, writes_count),
                http_read_buffer_,
                sub_protocol_,
                extensions_,
                generator_);
        }

        template <size_t N>
        void CreateReadOnly(MockRead (&reads)[N])
        {
            CreateStream(reads, N, NULL, 0);
        }

        void CreateNullStream() { CreateStream(NULL, 0, NULL, 0); }

        std::unique_ptr<SocketDataProvider> socket_data_;
        MockClientSocketFactory factory_;
        MockTransportClientSocketPool pool_;
        BoundTestNetLog(bound_net_log_);
        std::vector<std::unique_ptr<WebSocketFrame>> frames_;
        TestCompletionCallback cb_;
        scoped_refptr<GrowableIOBuffer> http_read_buffer_;
        std::string sub_protocol_;
        std::string extensions_;
        WebSocketBasicStream::WebSocketMaskingKeyGeneratorFunction generator_;
        bool expect_all_io_to_complete_;
    };

    // A test fixture for the common case of tests that only perform a single read.
    class WebSocketBasicStreamSocketSingleReadTest
        : public WebSocketBasicStreamSocketTest {
    protected:
        void CreateRead(const MockRead& read)
        {
            reads_[0] = read;
            CreateStream(reads_, 1U, NULL, 0);
        }

        MockRead reads_[1];
    };

    // A test fixture for tests that perform chunked reads.
    class WebSocketBasicStreamSocketChunkedReadTest
        : public WebSocketBasicStreamSocketTest {
    protected:
        // Specify the behaviour if there aren't enough chunks to use all the data. If
        // LAST_FRAME_BIG is specified, then the rest of the data will be
        // put in the last chunk. If LAST_FRAME_NOT_BIG is specified, then the last
        // frame will be no bigger than the rest of the frames (but it can be smaller,
        // if not enough data remains).
        enum LastFrameBehaviour {
            LAST_FRAME_BIG,
            LAST_FRAME_NOT_BIG
        };

        // Prepares a read from |data| of |data_size|, split into |number_of_chunks|,
        // each of |chunk_size| (except that the last chunk may be larger or
        // smaller). All reads must be either SYNCHRONOUS or ASYNC (not a mixture),
        // and errors cannot be simulated. Once data is exhausted, further reads will
        // return 0 (ie. connection closed).
        void CreateChunkedRead(IoMode mode,
            const char data[],
            size_t data_size,
            int chunk_size,
            int number_of_chunks,
            LastFrameBehaviour last_frame_behaviour)
        {
            reads_.reset(new MockRead[number_of_chunks]);
            const char* start = data;
            for (int i = 0; i < number_of_chunks; ++i) {
                int len = chunk_size;
                const bool is_last_chunk = (i == number_of_chunks - 1);
                if ((last_frame_behaviour == LAST_FRAME_BIG && is_last_chunk) || static_cast<int>(data + data_size - start) < len) {
                    len = static_cast<int>(data + data_size - start);
                }
                reads_[i] = MockRead(mode, start, len);
                start += len;
            }
            CreateStream(reads_.get(), number_of_chunks, NULL, 0);
        }

        std::unique_ptr<MockRead[]> reads_;
    };

    // Test fixture for write tests.
    class WebSocketBasicStreamSocketWriteTest
        : public WebSocketBasicStreamSocketTest {
    protected:
        // All write tests use the same frame, so it is easiest to create it during
        // test creation.
        void SetUp() override { PrepareWriteFrame(); }

        // Creates a WebSocketFrame with a wire format matching kWriteFrame and adds
        // it to |frames_|.
        void PrepareWriteFrame()
        {
            std::unique_ptr<WebSocketFrame> frame(
                new WebSocketFrame(WebSocketFrameHeader::kOpCodeText));
            const size_t payload_size = kWriteFrameSize - (WebSocketFrameHeader::kBaseHeaderSize + WebSocketFrameHeader::kMaskingKeyLength);
            frame->data = new IOBuffer(payload_size);
            memcpy(frame->data->data(),
                kWriteFrame + kWriteFrameSize - payload_size,
                payload_size);
            WebSocketFrameHeader& header = frame->header;
            header.final = true;
            header.masked = true;
            header.payload_length = payload_size;
            frames_.push_back(std::move(frame));
        }

        // Creates a stream that expects the listed writes.
        template <size_t N>
        void CreateWriteOnly(MockWrite (&writes)[N])
        {
            CreateStream(NULL, 0, writes, N);
        }
    };

    TEST_F(WebSocketBasicStreamSocketTest, ConstructionWorks)
    {
        CreateNullStream();
    }

    TEST_F(WebSocketBasicStreamSocketSingleReadTest, SyncReadWorks)
    {
        CreateRead(MockRead(SYNCHRONOUS, kSampleFrame, kSampleFrameSize));
        int result = stream_->ReadFrames(&frames_, cb_.callback());
        EXPECT_EQ(OK, result);
        ASSERT_EQ(1U, frames_.size());
        EXPECT_EQ(UINT64_C(6), frames_[0]->header.payload_length);
        EXPECT_TRUE(frames_[0]->header.final);
    }

    TEST_F(WebSocketBasicStreamSocketSingleReadTest, AsyncReadWorks)
    {
        CreateRead(MockRead(ASYNC, kSampleFrame, kSampleFrameSize));
        int result = stream_->ReadFrames(&frames_, cb_.callback());
        ASSERT_EQ(ERR_IO_PENDING, result);
        EXPECT_EQ(OK, cb_.WaitForResult());
        ASSERT_EQ(1U, frames_.size());
        EXPECT_EQ(UINT64_C(6), frames_[0]->header.payload_length);
        // Don't repeat all the tests from SyncReadWorks; just enough to be sure the
        // frame was really read.
    }

    // ReadFrames will not return a frame whose header has not been wholly received.
    TEST_F(WebSocketBasicStreamSocketChunkedReadTest, HeaderFragmentedSync)
    {
        CreateChunkedRead(
            SYNCHRONOUS, kSampleFrame, kSampleFrameSize, 1, 2, LAST_FRAME_BIG);
        int result = stream_->ReadFrames(&frames_, cb_.callback());
        EXPECT_EQ(OK, result);
        ASSERT_EQ(1U, frames_.size());
        EXPECT_EQ(UINT64_C(6), frames_[0]->header.payload_length);
    }

    // The same behaviour applies to asynchronous reads.
    TEST_F(WebSocketBasicStreamSocketChunkedReadTest, HeaderFragmentedAsync)
    {
        CreateChunkedRead(
            ASYNC, kSampleFrame, kSampleFrameSize, 1, 2, LAST_FRAME_BIG);
        int result = stream_->ReadFrames(&frames_, cb_.callback());
        ASSERT_EQ(ERR_IO_PENDING, result);
        EXPECT_EQ(OK, cb_.WaitForResult());
        ASSERT_EQ(1U, frames_.size());
        EXPECT_EQ(UINT64_C(6), frames_[0]->header.payload_length);
    }

    // If it receives an incomplete header in a synchronous call, then has to wait
    // for the rest of the frame, ReadFrames will return ERR_IO_PENDING.
    TEST_F(WebSocketBasicStreamSocketTest, HeaderFragmentedSyncAsync)
    {
        MockRead reads[] = { MockRead(SYNCHRONOUS, kSampleFrame, 1),
            MockRead(ASYNC, kSampleFrame + 1, kSampleFrameSize - 1) };
        CreateReadOnly(reads);
        int result = stream_->ReadFrames(&frames_, cb_.callback());
        ASSERT_EQ(ERR_IO_PENDING, result);
        EXPECT_EQ(OK, cb_.WaitForResult());
        ASSERT_EQ(1U, frames_.size());
        EXPECT_EQ(UINT64_C(6), frames_[0]->header.payload_length);
    }

    // An extended header should also return ERR_IO_PENDING if it is not completely
    // received.
    TEST_F(WebSocketBasicStreamSocketTest, FragmentedLargeHeader)
    {
        MockRead reads[] = {
            MockRead(SYNCHRONOUS, kPartialLargeFrame, kLargeFrameHeaderSize - 1),
            MockRead(SYNCHRONOUS, ERR_IO_PENDING)
        };
        CreateReadOnly(reads);
        EXPECT_EQ(ERR_IO_PENDING, stream_->ReadFrames(&frames_, cb_.callback()));
    }

    // A frame that does not arrive in a single read should be broken into separate
    // frames.
    TEST_F(WebSocketBasicStreamSocketSingleReadTest, LargeFrameFirstChunk)
    {
        CreateRead(MockRead(SYNCHRONOUS, kPartialLargeFrame, kPartialLargeFrameSize));
        EXPECT_EQ(OK, stream_->ReadFrames(&frames_, cb_.callback()));
        ASSERT_EQ(1U, frames_.size());
        EXPECT_FALSE(frames_[0]->header.final);
        EXPECT_EQ(kPartialLargeFrameSize - kLargeFrameHeaderSize,
            static_cast<size_t>(frames_[0]->header.payload_length));
    }

    // If only the header of a data frame arrives, we should receive a frame with a
    // zero-size payload.
    TEST_F(WebSocketBasicStreamSocketSingleReadTest, HeaderOnlyChunk)
    {
        CreateRead(MockRead(SYNCHRONOUS, kPartialLargeFrame, kLargeFrameHeaderSize));

        EXPECT_EQ(OK, stream_->ReadFrames(&frames_, cb_.callback()));
        ASSERT_EQ(1U, frames_.size());
        EXPECT_EQ(NULL, frames_[0]->data.get());
        EXPECT_EQ(0U, frames_[0]->header.payload_length);
        EXPECT_EQ(WebSocketFrameHeader::kOpCodeText, frames_[0]->header.opcode);
    }

    // If the header and the body of a data frame arrive seperately, we should see
    // them as separate frames.
    TEST_F(WebSocketBasicStreamSocketTest, HeaderBodySeparated)
    {
        MockRead reads[] = {
            MockRead(SYNCHRONOUS, kPartialLargeFrame, kLargeFrameHeaderSize),
            MockRead(ASYNC,
                kPartialLargeFrame + kLargeFrameHeaderSize,
                kPartialLargeFrameSize - kLargeFrameHeaderSize)
        };
        CreateReadOnly(reads);
        EXPECT_EQ(OK, stream_->ReadFrames(&frames_, cb_.callback()));
        ASSERT_EQ(1U, frames_.size());
        EXPECT_EQ(NULL, frames_[0]->data.get());
        EXPECT_EQ(WebSocketFrameHeader::kOpCodeText, frames_[0]->header.opcode);
        frames_.clear();
        EXPECT_EQ(ERR_IO_PENDING, stream_->ReadFrames(&frames_, cb_.callback()));
        EXPECT_EQ(OK, cb_.WaitForResult());
        ASSERT_EQ(1U, frames_.size());
        EXPECT_EQ(kPartialLargeFrameSize - kLargeFrameHeaderSize,
            frames_[0]->header.payload_length);
        EXPECT_EQ(WebSocketFrameHeader::kOpCodeContinuation,
            frames_[0]->header.opcode);
    }

    // Every frame has a header with a correct payload_length field.
    TEST_F(WebSocketBasicStreamSocketChunkedReadTest, LargeFrameTwoChunks)
    {
        const size_t kChunkSize = 16;
        CreateChunkedRead(ASYNC,
            kPartialLargeFrame,
            kPartialLargeFrameSize,
            kChunkSize,
            2,
            LAST_FRAME_NOT_BIG);
        TestCompletionCallback cb[2];

        ASSERT_EQ(ERR_IO_PENDING, stream_->ReadFrames(&frames_, cb[0].callback()));
        EXPECT_EQ(OK, cb[0].WaitForResult());
        ASSERT_EQ(1U, frames_.size());
        EXPECT_EQ(kChunkSize - kLargeFrameHeaderSize,
            frames_[0]->header.payload_length);

        frames_.clear();
        ASSERT_EQ(ERR_IO_PENDING, stream_->ReadFrames(&frames_, cb[1].callback()));
        EXPECT_EQ(OK, cb[1].WaitForResult());
        ASSERT_EQ(1U, frames_.size());
        EXPECT_EQ(kChunkSize, frames_[0]->header.payload_length);
    }

    // Only the final frame of a fragmented message has |final| bit set.
    TEST_F(WebSocketBasicStreamSocketChunkedReadTest, OnlyFinalChunkIsFinal)
    {
        static const size_t kFirstChunkSize = 4;
        CreateChunkedRead(ASYNC,
            kSampleFrame,
            kSampleFrameSize,
            kFirstChunkSize,
            2,
            LAST_FRAME_BIG);
        TestCompletionCallback cb[2];

        ASSERT_EQ(ERR_IO_PENDING, stream_->ReadFrames(&frames_, cb[0].callback()));
        EXPECT_EQ(OK, cb[0].WaitForResult());
        ASSERT_EQ(1U, frames_.size());
        ASSERT_FALSE(frames_[0]->header.final);

        frames_.clear();
        ASSERT_EQ(ERR_IO_PENDING, stream_->ReadFrames(&frames_, cb[1].callback()));
        EXPECT_EQ(OK, cb[1].WaitForResult());
        ASSERT_EQ(1U, frames_.size());
        ASSERT_TRUE(frames_[0]->header.final);
    }

    // All frames after the first have their opcode changed to Continuation.
    TEST_F(WebSocketBasicStreamSocketChunkedReadTest, ContinuationOpCodeUsed)
    {
        const size_t kFirstChunkSize = 3;
        const int kChunkCount = 3;
        // The input data is one frame with opcode Text, which arrives in three
        // separate chunks.
        CreateChunkedRead(ASYNC,
            kSampleFrame,
            kSampleFrameSize,
            kFirstChunkSize,
            kChunkCount,
            LAST_FRAME_BIG);
        TestCompletionCallback cb[kChunkCount];

        ASSERT_EQ(ERR_IO_PENDING, stream_->ReadFrames(&frames_, cb[0].callback()));
        EXPECT_EQ(OK, cb[0].WaitForResult());
        ASSERT_EQ(1U, frames_.size());
        EXPECT_EQ(WebSocketFrameHeader::kOpCodeText, frames_[0]->header.opcode);

        // This test uses a loop to verify that the opcode for every frames generated
        // after the first is converted to Continuation.
        for (int i = 1; i < kChunkCount; ++i) {
            frames_.clear();
            ASSERT_EQ(ERR_IO_PENDING, stream_->ReadFrames(&frames_, cb[i].callback()));
            EXPECT_EQ(OK, cb[i].WaitForResult());
            ASSERT_EQ(1U, frames_.size());
            EXPECT_EQ(WebSocketFrameHeader::kOpCodeContinuation,
                frames_[0]->header.opcode);
        }
    }

    // Multiple frames that arrive together should be parsed correctly.
    TEST_F(WebSocketBasicStreamSocketSingleReadTest, ThreeFramesTogether)
    {
        CreateRead(MockRead(SYNCHRONOUS, kMultipleFrames, kMultipleFramesSize));

        EXPECT_EQ(OK, stream_->ReadFrames(&frames_, cb_.callback()));
        ASSERT_EQ(3U, frames_.size());
        EXPECT_TRUE(frames_[0]->header.final);
        EXPECT_TRUE(frames_[1]->header.final);
        EXPECT_TRUE(frames_[2]->header.final);
    }

    // ERR_CONNECTION_CLOSED must be returned on close.
    TEST_F(WebSocketBasicStreamSocketSingleReadTest, SyncClose)
    {
        CreateRead(MockRead(SYNCHRONOUS, "", 0));

        EXPECT_EQ(ERR_CONNECTION_CLOSED,
            stream_->ReadFrames(&frames_, cb_.callback()));
    }

    TEST_F(WebSocketBasicStreamSocketSingleReadTest, AsyncClose)
    {
        CreateRead(MockRead(ASYNC, "", 0));

        ASSERT_EQ(ERR_IO_PENDING, stream_->ReadFrames(&frames_, cb_.callback()));
        EXPECT_EQ(ERR_CONNECTION_CLOSED, cb_.WaitForResult());
    }

    // The result should be the same if the socket returns
    // ERR_CONNECTION_CLOSED. This is not expected to happen on an established
    // connection; a Read of size 0 is the expected behaviour. The key point of this
    // test is to confirm that ReadFrames() behaviour is identical in both cases.
    TEST_F(WebSocketBasicStreamSocketSingleReadTest, SyncCloseWithErr)
    {
        CreateRead(MockRead(SYNCHRONOUS, ERR_CONNECTION_CLOSED));

        EXPECT_EQ(ERR_CONNECTION_CLOSED,
            stream_->ReadFrames(&frames_, cb_.callback()));
    }

    TEST_F(WebSocketBasicStreamSocketSingleReadTest, AsyncCloseWithErr)
    {
        CreateRead(MockRead(ASYNC, ERR_CONNECTION_CLOSED));

        ASSERT_EQ(ERR_IO_PENDING, stream_->ReadFrames(&frames_, cb_.callback()));
        EXPECT_EQ(ERR_CONNECTION_CLOSED, cb_.WaitForResult());
    }

    TEST_F(WebSocketBasicStreamSocketSingleReadTest, SyncErrorsPassedThrough)
    {
        // ERR_INSUFFICIENT_RESOURCES here represents an arbitrary error that
        // WebSocketBasicStream gives no special handling to.
        CreateRead(MockRead(SYNCHRONOUS, ERR_INSUFFICIENT_RESOURCES));

        EXPECT_EQ(ERR_INSUFFICIENT_RESOURCES,
            stream_->ReadFrames(&frames_, cb_.callback()));
    }

    TEST_F(WebSocketBasicStreamSocketSingleReadTest, AsyncErrorsPassedThrough)
    {
        CreateRead(MockRead(ASYNC, ERR_INSUFFICIENT_RESOURCES));

        ASSERT_EQ(ERR_IO_PENDING, stream_->ReadFrames(&frames_, cb_.callback()));
        EXPECT_EQ(ERR_INSUFFICIENT_RESOURCES, cb_.WaitForResult());
    }

    // If we get a frame followed by a close, we should receive them separately.
    TEST_F(WebSocketBasicStreamSocketChunkedReadTest, CloseAfterFrame)
    {
        // The chunk size equals the data size, so the second chunk is 0 size, closing
        // the connection.
        CreateChunkedRead(SYNCHRONOUS,
            kSampleFrame,
            kSampleFrameSize,
            kSampleFrameSize,
            2,
            LAST_FRAME_NOT_BIG);

        EXPECT_EQ(OK, stream_->ReadFrames(&frames_, cb_.callback()));
        EXPECT_EQ(1U, frames_.size());
        frames_.clear();
        EXPECT_EQ(ERR_CONNECTION_CLOSED,
            stream_->ReadFrames(&frames_, cb_.callback()));
    }

    // Synchronous close after an async frame header is handled by a different code
    // path.
    TEST_F(WebSocketBasicStreamSocketTest, AsyncCloseAfterIncompleteHeader)
    {
        MockRead reads[] = { MockRead(ASYNC, kSampleFrame, 1U),
            MockRead(SYNCHRONOUS, "", 0) };
        CreateReadOnly(reads);

        ASSERT_EQ(ERR_IO_PENDING, stream_->ReadFrames(&frames_, cb_.callback()));
        EXPECT_EQ(ERR_CONNECTION_CLOSED, cb_.WaitForResult());
    }

    // When Stream::Read returns ERR_CONNECTION_CLOSED we get the same result via a
    // slightly different code path.
    TEST_F(WebSocketBasicStreamSocketTest, AsyncErrCloseAfterIncompleteHeader)
    {
        MockRead reads[] = { MockRead(ASYNC, kSampleFrame, 1U),
            MockRead(SYNCHRONOUS, ERR_CONNECTION_CLOSED) };
        CreateReadOnly(reads);

        ASSERT_EQ(ERR_IO_PENDING, stream_->ReadFrames(&frames_, cb_.callback()));
        EXPECT_EQ(ERR_CONNECTION_CLOSED, cb_.WaitForResult());
    }

    // An empty first frame is not ignored.
    TEST_F(WebSocketBasicStreamSocketSingleReadTest, EmptyFirstFrame)
    {
        CreateRead(MockRead(SYNCHRONOUS, kEmptyFirstFrame, kEmptyFirstFrameSize));

        EXPECT_EQ(OK, stream_->ReadFrames(&frames_, cb_.callback()));
        ASSERT_EQ(1U, frames_.size());
        EXPECT_EQ(NULL, frames_[0]->data.get());
        EXPECT_EQ(0U, frames_[0]->header.payload_length);
    }

    // An empty frame in the middle of a message is ignored.
    TEST_F(WebSocketBasicStreamSocketTest, EmptyMiddleFrame)
    {
        MockRead reads[] = {
            MockRead(SYNCHRONOUS, kEmptyFirstFrame, kEmptyFirstFrameSize),
            MockRead(SYNCHRONOUS, kEmptyMiddleFrame, kEmptyMiddleFrameSize),
            MockRead(SYNCHRONOUS, ERR_IO_PENDING)
        };
        CreateReadOnly(reads);

        EXPECT_EQ(OK, stream_->ReadFrames(&frames_, cb_.callback()));
        EXPECT_EQ(1U, frames_.size());
        frames_.clear();
        EXPECT_EQ(ERR_IO_PENDING, stream_->ReadFrames(&frames_, cb_.callback()));
    }

    // An empty frame in the middle of a message that arrives separately is still
    // ignored.
    TEST_F(WebSocketBasicStreamSocketTest, EmptyMiddleFrameAsync)
    {
        MockRead reads[] = {
            MockRead(SYNCHRONOUS, kEmptyFirstFrame, kEmptyFirstFrameSize),
            MockRead(ASYNC, kEmptyMiddleFrame, kEmptyMiddleFrameSize),
            // We include a pong message to verify the middle frame was actually
            // processed.
            MockRead(ASYNC, kValidPong, kValidPongSize)
        };
        CreateReadOnly(reads);

        EXPECT_EQ(OK, stream_->ReadFrames(&frames_, cb_.callback()));
        EXPECT_EQ(1U, frames_.size());
        frames_.clear();
        ASSERT_EQ(ERR_IO_PENDING, stream_->ReadFrames(&frames_, cb_.callback()));
        EXPECT_EQ(OK, cb_.WaitForResult());
        ASSERT_EQ(1U, frames_.size());
        EXPECT_EQ(WebSocketFrameHeader::kOpCodePong, frames_[0]->header.opcode);
    }

    // An empty final frame is not ignored.
    TEST_F(WebSocketBasicStreamSocketSingleReadTest, EmptyFinalFrame)
    {
        CreateRead(
            MockRead(SYNCHRONOUS, kEmptyFinalTextFrame, kEmptyFinalTextFrameSize));

        EXPECT_EQ(OK, stream_->ReadFrames(&frames_, cb_.callback()));
        ASSERT_EQ(1U, frames_.size());
        EXPECT_EQ(NULL, frames_[0]->data.get());
        EXPECT_EQ(0U, frames_[0]->header.payload_length);
    }

    // An empty middle frame is ignored with a final frame present.
    TEST_F(WebSocketBasicStreamSocketTest, ThreeFrameEmptyMessage)
    {
        MockRead reads[] = {
            MockRead(SYNCHRONOUS, kEmptyFirstFrame, kEmptyFirstFrameSize),
            MockRead(SYNCHRONOUS, kEmptyMiddleFrame, kEmptyMiddleFrameSize),
            MockRead(SYNCHRONOUS,
                kEmptyFinalContinuationFrame,
                kEmptyFinalContinuationFrameSize)
        };
        CreateReadOnly(reads);

        EXPECT_EQ(OK, stream_->ReadFrames(&frames_, cb_.callback()));
        ASSERT_EQ(1U, frames_.size());
        EXPECT_EQ(WebSocketFrameHeader::kOpCodeText, frames_[0]->header.opcode);
        frames_.clear();
        EXPECT_EQ(OK, stream_->ReadFrames(&frames_, cb_.callback()));
        ASSERT_EQ(1U, frames_.size());
        EXPECT_TRUE(frames_[0]->header.final);
    }

    // If there was a frame read at the same time as the response headers (and the
    // handshake succeeded), then we should parse it.
    TEST_F(WebSocketBasicStreamSocketTest, HttpReadBufferIsUsed)
    {
        SetHttpReadBuffer(kSampleFrame, kSampleFrameSize);
        CreateNullStream();

        EXPECT_EQ(OK, stream_->ReadFrames(&frames_, cb_.callback()));
        ASSERT_EQ(1U, frames_.size());
        ASSERT_TRUE(frames_[0]->data.get());
        EXPECT_EQ(UINT64_C(6), frames_[0]->header.payload_length);
    }

    // Check that a frame whose header partially arrived at the end of the response
    // headers works correctly.
    TEST_F(WebSocketBasicStreamSocketSingleReadTest,
        PartialFrameHeaderInHttpResponse)
    {
        SetHttpReadBuffer(kSampleFrame, 1);
        CreateRead(MockRead(ASYNC, kSampleFrame + 1, kSampleFrameSize - 1));

        ASSERT_EQ(ERR_IO_PENDING, stream_->ReadFrames(&frames_, cb_.callback()));
        EXPECT_EQ(OK, cb_.WaitForResult());
        ASSERT_EQ(1U, frames_.size());
        ASSERT_TRUE(frames_[0]->data.get());
        EXPECT_EQ(UINT64_C(6), frames_[0]->header.payload_length);
        EXPECT_EQ(WebSocketFrameHeader::kOpCodeText, frames_[0]->header.opcode);
    }

    // Check that a control frame which partially arrives at the end of the response
    // headers works correctly.
    TEST_F(WebSocketBasicStreamSocketSingleReadTest,
        PartialControlFrameInHttpResponse)
    {
        const size_t kPartialFrameBytes = 3;
        SetHttpReadBuffer(kCloseFrame, kPartialFrameBytes);
        CreateRead(MockRead(ASYNC,
            kCloseFrame + kPartialFrameBytes,
            kCloseFrameSize - kPartialFrameBytes));

        ASSERT_EQ(ERR_IO_PENDING, stream_->ReadFrames(&frames_, cb_.callback()));
        EXPECT_EQ(OK, cb_.WaitForResult());
        ASSERT_EQ(1U, frames_.size());
        EXPECT_EQ(WebSocketFrameHeader::kOpCodeClose, frames_[0]->header.opcode);
        EXPECT_EQ(kCloseFrameSize - 2, frames_[0]->header.payload_length);
        EXPECT_EQ(
            0,
            memcmp(frames_[0]->data->data(), kCloseFrame + 2, kCloseFrameSize - 2));
    }

    // Check that a control frame which partially arrives at the end of the response
    // headers works correctly. Synchronous version (unlikely in practice).
    TEST_F(WebSocketBasicStreamSocketSingleReadTest,
        PartialControlFrameInHttpResponseSync)
    {
        const size_t kPartialFrameBytes = 3;
        SetHttpReadBuffer(kCloseFrame, kPartialFrameBytes);
        CreateRead(MockRead(SYNCHRONOUS,
            kCloseFrame + kPartialFrameBytes,
            kCloseFrameSize - kPartialFrameBytes));

        EXPECT_EQ(OK, stream_->ReadFrames(&frames_, cb_.callback()));
        ASSERT_EQ(1U, frames_.size());
        EXPECT_EQ(WebSocketFrameHeader::kOpCodeClose, frames_[0]->header.opcode);
    }

    // Check that an invalid frame results in an error.
    TEST_F(WebSocketBasicStreamSocketSingleReadTest, SyncInvalidFrame)
    {
        CreateRead(MockRead(SYNCHRONOUS, kInvalidFrame, kInvalidFrameSize));

        EXPECT_EQ(ERR_WS_PROTOCOL_ERROR,
            stream_->ReadFrames(&frames_, cb_.callback()));
    }

    TEST_F(WebSocketBasicStreamSocketSingleReadTest, AsyncInvalidFrame)
    {
        CreateRead(MockRead(ASYNC, kInvalidFrame, kInvalidFrameSize));

        ASSERT_EQ(ERR_IO_PENDING, stream_->ReadFrames(&frames_, cb_.callback()));
        EXPECT_EQ(ERR_WS_PROTOCOL_ERROR, cb_.WaitForResult());
    }

    // A control frame without a FIN flag is invalid and should not be passed
    // through to higher layers. RFC6455 5.5 "All control frames ... MUST NOT be
    // fragmented."
    TEST_F(WebSocketBasicStreamSocketSingleReadTest, ControlFrameWithoutFin)
    {
        CreateRead(
            MockRead(SYNCHRONOUS, kPingFrameWithoutFin, kPingFrameWithoutFinSize));

        EXPECT_EQ(ERR_WS_PROTOCOL_ERROR,
            stream_->ReadFrames(&frames_, cb_.callback()));
        EXPECT_TRUE(frames_.empty());
    }

    // A control frame over 125 characters is invalid. RFC6455 5.5 "All control
    // frames MUST have a payload length of 125 bytes or less". Since we use a
    // 125-byte buffer to assemble fragmented control frames, we need to detect this
    // error before attempting to assemble the fragments.
    TEST_F(WebSocketBasicStreamSocketSingleReadTest, OverlongControlFrame)
    {
        CreateRead(MockRead(SYNCHRONOUS, k126BytePong, k126BytePongSize));

        EXPECT_EQ(ERR_WS_PROTOCOL_ERROR,
            stream_->ReadFrames(&frames_, cb_.callback()));
        EXPECT_TRUE(frames_.empty());
    }

    // A control frame over 125 characters should still be rejected if it is split
    // into multiple chunks.
    TEST_F(WebSocketBasicStreamSocketChunkedReadTest, SplitOverlongControlFrame)
    {
        const size_t kFirstChunkSize = 16;
        expect_all_io_to_complete_ = false;
        CreateChunkedRead(SYNCHRONOUS,
            k126BytePong,
            k126BytePongSize,
            kFirstChunkSize,
            2,
            LAST_FRAME_BIG);

        EXPECT_EQ(ERR_WS_PROTOCOL_ERROR,
            stream_->ReadFrames(&frames_, cb_.callback()));
        EXPECT_TRUE(frames_.empty());
    }

    TEST_F(WebSocketBasicStreamSocketChunkedReadTest,
        AsyncSplitOverlongControlFrame)
    {
        const size_t kFirstChunkSize = 16;
        expect_all_io_to_complete_ = false;
        CreateChunkedRead(ASYNC,
            k126BytePong,
            k126BytePongSize,
            kFirstChunkSize,
            2,
            LAST_FRAME_BIG);

        ASSERT_EQ(ERR_IO_PENDING, stream_->ReadFrames(&frames_, cb_.callback()));
        EXPECT_EQ(ERR_WS_PROTOCOL_ERROR, cb_.WaitForResult());
        // The caller should not call ReadFrames() again after receiving an error
        // other than ERR_IO_PENDING.
        EXPECT_TRUE(frames_.empty());
    }

    // In the synchronous case, ReadFrames assembles the whole control frame before
    // returning.
    TEST_F(WebSocketBasicStreamSocketChunkedReadTest, SyncControlFrameAssembly)
    {
        const size_t kChunkSize = 3;
        CreateChunkedRead(
            SYNCHRONOUS, kCloseFrame, kCloseFrameSize, kChunkSize, 3, LAST_FRAME_BIG);

        EXPECT_EQ(OK, stream_->ReadFrames(&frames_, cb_.callback()));
        ASSERT_EQ(1U, frames_.size());
        EXPECT_EQ(WebSocketFrameHeader::kOpCodeClose, frames_[0]->header.opcode);
    }

    // In the asynchronous case, the callback is not called until the control frame
    // has been completely assembled.
    TEST_F(WebSocketBasicStreamSocketChunkedReadTest, AsyncControlFrameAssembly)
    {
        const size_t kChunkSize = 3;
        CreateChunkedRead(
            ASYNC, kCloseFrame, kCloseFrameSize, kChunkSize, 3, LAST_FRAME_BIG);

        ASSERT_EQ(ERR_IO_PENDING, stream_->ReadFrames(&frames_, cb_.callback()));
        EXPECT_EQ(OK, cb_.WaitForResult());
        ASSERT_EQ(1U, frames_.size());
        EXPECT_EQ(WebSocketFrameHeader::kOpCodeClose, frames_[0]->header.opcode);
    }

    // A frame with a 1MB payload that has to be read in chunks.
    TEST_F(WebSocketBasicStreamSocketChunkedReadTest, OneMegFrame)
    {
        // This should be equal to the definition of kReadBufferSize in
        // websocket_basic_stream.cc.
        const int kReadBufferSize = 32 * 1024;
        const uint64_t kPayloadSize = 1 << 20;
        const size_t kWireSize = kPayloadSize + kLargeFrameHeaderSize;
        const size_t kExpectedFrameCount = (kWireSize + kReadBufferSize - 1) / kReadBufferSize;
        std::unique_ptr<char[]> big_frame(new char[kWireSize]);
        memcpy(big_frame.get(), "\x81\x7F", 2);
        base::WriteBigEndian(big_frame.get() + 2, kPayloadSize);
        memset(big_frame.get() + kLargeFrameHeaderSize, 'A', kPayloadSize);

        CreateChunkedRead(ASYNC,
            big_frame.get(),
            kWireSize,
            kReadBufferSize,
            kExpectedFrameCount,
            LAST_FRAME_BIG);

        for (size_t frame = 0; frame < kExpectedFrameCount; ++frame) {
            frames_.clear();
            ASSERT_EQ(ERR_IO_PENDING, stream_->ReadFrames(&frames_, cb_.callback()));
            EXPECT_EQ(OK, cb_.WaitForResult());
            ASSERT_EQ(1U, frames_.size());
            size_t expected_payload_size = kReadBufferSize;
            if (frame == 0) {
                expected_payload_size = kReadBufferSize - kLargeFrameHeaderSize;
            } else if (frame == kExpectedFrameCount - 1) {
                expected_payload_size = kLargeFrameHeaderSize;
            }
            EXPECT_EQ(expected_payload_size, frames_[0]->header.payload_length);
        }
    }

    // A frame with reserved flag(s) set that arrives in chunks should only have the
    // reserved flag(s) set on the first chunk when split.
    TEST_F(WebSocketBasicStreamSocketChunkedReadTest, ReservedFlagCleared)
    {
        static const char kReservedFlagFrame[] = "\x41\x05Hello";
        const size_t kReservedFlagFrameSize = arraysize(kReservedFlagFrame) - 1;
        const size_t kChunkSize = 5;

        CreateChunkedRead(ASYNC,
            kReservedFlagFrame,
            kReservedFlagFrameSize,
            kChunkSize,
            2,
            LAST_FRAME_BIG);

        TestCompletionCallback cb[2];
        ASSERT_EQ(ERR_IO_PENDING, stream_->ReadFrames(&frames_, cb[0].callback()));
        EXPECT_EQ(OK, cb[0].WaitForResult());
        ASSERT_EQ(1U, frames_.size());
        EXPECT_TRUE(frames_[0]->header.reserved1);

        frames_.clear();
        ASSERT_EQ(ERR_IO_PENDING, stream_->ReadFrames(&frames_, cb[1].callback()));
        EXPECT_EQ(OK, cb[1].WaitForResult());
        ASSERT_EQ(1U, frames_.size());
        EXPECT_FALSE(frames_[0]->header.reserved1);
    }

    // Check that writing a frame all at once works.
    TEST_F(WebSocketBasicStreamSocketWriteTest, WriteAtOnce)
    {
        MockWrite writes[] = { MockWrite(SYNCHRONOUS, kWriteFrame, kWriteFrameSize) };
        CreateWriteOnly(writes);

        EXPECT_EQ(OK, stream_->WriteFrames(&frames_, cb_.callback()));
    }

    // Check that completely async writing works.
    TEST_F(WebSocketBasicStreamSocketWriteTest, AsyncWriteAtOnce)
    {
        MockWrite writes[] = { MockWrite(ASYNC, kWriteFrame, kWriteFrameSize) };
        CreateWriteOnly(writes);

        ASSERT_EQ(ERR_IO_PENDING, stream_->WriteFrames(&frames_, cb_.callback()));
        EXPECT_EQ(OK, cb_.WaitForResult());
    }

    // Check that writing a frame to an extremely full kernel buffer (so that it
    // ends up being sent in bits) works. The WriteFrames() callback should not be
    // called until all parts have been written.
    TEST_F(WebSocketBasicStreamSocketWriteTest, WriteInBits)
    {
        MockWrite writes[] = { MockWrite(SYNCHRONOUS, kWriteFrame, 4),
            MockWrite(ASYNC, kWriteFrame + 4, 4),
            MockWrite(ASYNC, kWriteFrame + 8, kWriteFrameSize - 8) };
        CreateWriteOnly(writes);

        ASSERT_EQ(ERR_IO_PENDING, stream_->WriteFrames(&frames_, cb_.callback()));
        EXPECT_EQ(OK, cb_.WaitForResult());
    }

    // Check that writing a Pong frame with a NULL body works.
    TEST_F(WebSocketBasicStreamSocketWriteTest, WriteNullPong)
    {
        MockWrite writes[] = {
            MockWrite(SYNCHRONOUS, kMaskedEmptyPong, kMaskedEmptyPongSize)
        };
        CreateWriteOnly(writes);

        std::unique_ptr<WebSocketFrame> frame(
            new WebSocketFrame(WebSocketFrameHeader::kOpCodePong));
        WebSocketFrameHeader& header = frame->header;
        header.final = true;
        header.masked = true;
        header.payload_length = 0;
        std::vector<std::unique_ptr<WebSocketFrame>> frames;
        frames.push_back(std::move(frame));
        EXPECT_EQ(OK, stream_->WriteFrames(&frames, cb_.callback()));
    }

    // Check that writing with a non-NULL mask works correctly.
    TEST_F(WebSocketBasicStreamSocketTest, WriteNonNulMask)
    {
        std::string masked_frame = std::string("\x81\x88");
        masked_frame += std::string(kNonNulMaskingKey.key, 4);
        masked_frame += "jiggered";
        MockWrite writes[] = {
            MockWrite(SYNCHRONOUS, masked_frame.data(), masked_frame.size())
        };
        generator_ = &GenerateNonNulMaskingKey;
        CreateStream(NULL, 0, writes, arraysize(writes));

        std::unique_ptr<WebSocketFrame> frame(
            new WebSocketFrame(WebSocketFrameHeader::kOpCodeText));
        const std::string unmasked_payload = "graphics";
        const size_t payload_size = unmasked_payload.size();
        frame->data = new IOBuffer(payload_size);
        memcpy(frame->data->data(), unmasked_payload.data(), payload_size);
        WebSocketFrameHeader& header = frame->header;
        header.final = true;
        header.masked = true;
        header.payload_length = payload_size;
        frames_.push_back(std::move(frame));

        EXPECT_EQ(OK, stream_->WriteFrames(&frames_, cb_.callback()));
    }

    TEST_F(WebSocketBasicStreamSocketTest, GetExtensionsWorks)
    {
        extensions_ = "inflate-uuencode";
        CreateNullStream();

        EXPECT_EQ("inflate-uuencode", stream_->GetExtensions());
    }

    TEST_F(WebSocketBasicStreamSocketTest, GetSubProtocolWorks)
    {
        sub_protocol_ = "cyberchat";
        CreateNullStream();

        EXPECT_EQ("cyberchat", stream_->GetSubProtocol());
    }

} // namespace
} // namespace net
